/**
 * Main entry of lan server.
 */
var globalConfigs = require("./globalConfigs.js");

function getLocalIPAddress() {
	"use strict";
	var os = require("os");
	var nics = os.networkInterfaces();

	for ( var k in nics) {
		if (nics.hasOwnProperty(k)) {

			var addressArray = nics[k];
			var length = addressArray.length;
			for (var ix = 0; ix < length; ix = ix + 1) {
				var iff = addressArray[ix];
				if (iff.internal === false && iff.family === "IPv4") {
					return iff.address;
				}
			}
		}
	}

	return null;
}

if (undefined === globalConfigs.thisIP) {
	globalConfigs.thisIP = getLocalIPAddress();
	if (!globalConfigs.thisIP) {
		console.error("failed to get local ipv4 address, can't start server");
		process.exit(1);
	}
}

/**
 * init redis client system.
 * 
 * @param callback
 *            callback when completed
 */
function initRedis(callback) {
	"use strict";
	var redis = require("./Redis/redis.js");
	redis.startup(function(result) {
		if (result) {
			console.log("init redis ok");
		} else {
			console.log("init redis failed");
		}
		callback(result);
	});
}

function readHandlers(dir, tag) {
	"use strict";
	var handlers = {};
	var fs = require("fs");
	var files = fs.readdirSync(dir);
	files.forEach(function(fileName) {
		var lexport = require(dir + "/" + fileName);
		var t = lexport[tag];
		if (t) {
			handlers[t] = lexport;
		}
	});

	return handlers;
}
//var dispatcher = require("./Dev/dispatcher.js");
//dispatcher.setCmdHandlers(readHandlers("./DevHandlers", "cmdCode"));
var httpHandlers = readHandlers("./UserHandlers", "url");
var genTok = require("./token/gentok.js");
var urlEncode = require("url");
function verifyToken(req) {
	"use strict";
	var token = req.headers.token;
	if (!token) {
		return null;
	}

	return genTok.parse(token);
}

/**
 * handle http request.
 * 
 * @param req
 *            request object
 * @param res
 *            response object
 */
function handleHttpRequest(req, res) {
	"use strict";
	var body = "";
	req.on("data", function(data) {
		body += data;

		// Too much POST data, kill the connection!
		if (body.length > 1e6) {
			req.connection.destroy();
		}
	});
	req.on("end", function() {
		req.body = body;
		var url = urlEncode.parse(req.url).pathname;
		var handler = httpHandlers[url];
		if (handler) {
			if (handler.tokenFree) {
				handler(req, res);
			} else {
				var v = verifyToken(req);
				if (!v || v.expired) {
					res.writeHead(200, {
						"error" : 10
					});
					res.end();
				} else {
					handler(req, res, v.account);
				}
			}
		} else {
			res.writeHead(404, {
				"Content-Type" : "text/plain"
			});
			res.end("Hello World\n");
		}
	});
}
var expressMethods = {};
function cookieVerifyHook(req, res) {
	"use strict";
	var cookies = req.cookies;
	// console.error(cookies);
	if (!cookies || !cookies.tk) {
		res.redirect("/login");
		return;
	}

	var tk = cookies.tk;
	var xp = genTok.parse(tk);
	// console.error(xp);
	if (!xp || xp.expired) {
		res.redirect("/login");
		return;
	}

	var url = urlEncode.parse(req.url).pathname;
	var handler = expressMethods[url];
	if (handler) {
		handler(req, res);
	} else {
		res.writeHead(404, {
			"Content-Type" : "text/plain"
		});
		res.end(url + " Page not found\n");
	}
}
/**
 * start express server for web browser access.
 */
function startExpressServer() {
	"use strict";
	var express = require("express");
	var app = express();
	var routes = require("./routes");
	var http = require("http"), path = require("path");

	// all environments
	app.set("port", (globalConfigs.ePort));
	app.set("views", __dirname + "/views");
	app.set("view engine", "ejs");

	var morgan = require("morgan");
	var bodyParser = require("body-parser");
	var methodOverride = require("method-override");
	var multipart = require("connect-multiparty");
	var multipartMiddleware = multipart({
		keepExtensions : true,
		uploadDir : path.join(__dirname, "/public/tmp")
	});
	app.use(multipartMiddleware);
	var cookieParser = require("cookie-parser");
	app.use(cookieParser());
	app.use(express.static(__dirname + "/public"));
	app.use(morgan("dev")); // for logger
	app.use(methodOverride());

	app.use(bodyParser.json());
	app.use(bodyParser.urlencoded({
		extended : true
	}));
	//app.use(bodyParser());

	// development only
	if ("development" === app.get("env")) {
		var errorHandler = require("errorhandler");
		app.use(errorHandler());
	}

	expressMethods["/"] = routes.index;
	app.get("/", cookieVerifyHook);

	for ( var k in routes) {
		if (routes.hasOwnProperty(k)) {
			var f = routes[k];
			var xf;
			if (!f.cookieFree) {
				expressMethods["/" + k] = f;
				xf = cookieVerifyHook;
			} else {
				xf = f;
			}
			if (typeof f === "function") {
				if (k.charAt(0) === "_") {
					app.post("/" + k, xf);
				} else {
					app.get("/" + k, xf);
				}
			}
		}
	}

	http.createServer(app).listen(app.get("port"), function() {
		console.log("Express server listening on port " + app.get("port"));
	});
}

var wsConnectHandler = require("./WSHandlers/wsConnectHandler.js");

/**
 * start HTTP and WEBSOCKET server.
 */
function startHttpServer() {
	"use strict";
	var http = require("http");
	var httpServer = http.createServer(handleHttpRequest);
	var ip = "0.0.0.0";
	var port = (globalConfigs.wPort);
	httpServer.listen(port, ip, function() {
		var WebSocket = require("ws");
		var webSockServer = new WebSocket.Server({
			server : httpServer,
			path : "/ws"
		});
		webSockServer.on("connection", function(socket) {
			wsConnectHandler(socket);
		});

		startExpressServer();

		console.log("Http/WebSocket Server running at http://" + ip + ":"
				+ port + "/");

		process.emit("ServerStarted");
	});
}

//var connectHandler = require("./DevHandlers/connectHandler.js");
//function startTcpServer() {
//	"use strict";
//	var net = require("net");
//	var server = net.createServer(connectHandler);
//	server.listen(globalConfigs.tPort, function() { // "listening" listener
//		console.log("TCP server bound:" + globalConfigs.tPort);
//		startHttpServer();
//	});
//}

/**
 * main entry function.
 */
function main() {
	"use strict";

	initRedis(function(result) {
		if (result) {
			startExpressServer();
		}
	});
}
main();
